package uk.ac.open.kmi.dyniqx.repositories;

import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;

import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
import org.apache.commons.httpclient.HostConfiguration;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.log4j.Logger;
import org.apache.lucene.document.Field;
import org.apache.xalan.xsltc.trax.SAX2DOM;
import org.apache.xpath.XPathAPI;
import org.ccil.cowan.tagsoup.Parser;
import org.directwebremoting.WebContext;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;

import uk.ac.open.kmi.dyniqx.storages.Storage;

public class GoogleScholarThread extends Thread {
	
	public String queryString;
	public int count;

	public Storage storage;
	public WebContext wctx;
	
	public volatile boolean stop = false;
	
	public Logger logger = Logger.getLogger("uk.ac.open.kmi.dyniqx.repositories.GoogleScholarThread");
	
	public GoogleScholarThread(Storage storage, WebContext wctx, String queryString, int count) {
		
		this.setStorage(storage);
		this.setWctx(wctx);
		this.setQueryString(queryString);
		this.setCount(count);
		 
	}

	@SuppressWarnings("unchecked")
	public void run() {
		
		this.logger.debug("Thread started.");
		
		String searchUrl = "http://scholar.google.co.uk/scholar?num=100&q=";
		if (this.count > 100) this.count = 100;

		/* Open University proxy Settings */
		  
		System.getProperties().put( "proxySet", "true" );
		System.getProperties().put( "proxyHost", "wwwcache" );
		System.getProperties().put( "proxyPort", "80" );
		System.setProperty("java.net.useSystemProxies","true");

		HttpClient client = new HttpClient();
		HostConfiguration hhc = client.getHostConfiguration();
		
		/* Open University proxy Settings */
		  
	    hhc.setProxy("wwwcache", 80);

		client.setHostConfiguration(hhc);
		
		try {

			GetMethod method = new GetMethod(searchUrl + URLEncoder.encode(queryString, "UTF-8"));

			method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler(3, false));
		    
			int statusCode = client.executeMethod(method);
		    
			if (statusCode != HttpStatus.SC_OK) {
		        System.err.println("Method failed: " + method.getStatusLine());
		    }
	    
		    Parser p = new Parser();
		    p.setFeature("http://xml.org/sax/features/namespace-prefixes",true);
		    
		    SAX2DOM sax2dom = new SAX2DOM();
		    p.setContentHandler(sax2dom);
		    
		    p.parse(new InputSource(method.getResponseBodyAsStream()));
		    
		    Node doc = sax2dom.getDOM();
		    NodeList nl = XPathAPI.selectNodeList(doc, "/html:html//html:p[@class='g']");
		    
		    org.apache.lucene.document.Document result = null;
		    ArrayList<org.apache.lucene.document.Document> documentArray = new ArrayList<org.apache.lucene.document.Document>();
		    
		    for (int i = 0; i < this.count; i++) {
		    				   	    	
		    	Node current = nl.item(i);
		    			        
		    	if (current.getFirstChild().getNodeName().equals("span")) {
		    		String title = XPathAPI.selectSingleNode(current, "html:span[@class='w']/html:a").getTextContent();			    	 
			    	String link = XPathAPI.selectSingleNode(current, "html:span[@class='w']/html:a/@href").getTextContent();
			    	String description = "";
			    	NodeList descriptionNodes = XPathAPI.selectSingleNode(current, "html:font[@size='-1']").getChildNodes();
			    	
			    	for (int j = 0; j < descriptionNodes.getLength(); j++) {
			    		Node localeNode = descriptionNodes.item(j);
			    		if (localeNode.getNodeName().equals("#text") && !localeNode.getTextContent().equals(" - ")) {
			    			description += localeNode.getTextContent().trim();
			    		}
			    	}
			    				    	
			    	String authors = XPathAPI.selectSingleNode(current, "html:font[@size='-1']/html:span[@class='a']").getTextContent();			    	 
			    	
			    	result = new org.apache.lucene.document.Document();

			        result.add(new Field("source", "GSc", Field.Store.YES, Field.Index.UN_TOKENIZED));
			        result.add(new Field("type", "text", Field.Store.YES, Field.Index.UN_TOKENIZED));
			        result.add(new Field("id", title, Field.Store.YES, Field.Index.UN_TOKENIZED));
			        result.add(new Field("query", this.queryString, Field.Store.YES, Field.Index.UN_TOKENIZED));
			        result.add(new Field("rank", Integer.toString(i+1), Field.Store.YES, Field.Index.UN_TOKENIZED));
			        			        
			        result.add(new Field("link", link, Field.Store.YES, Field.Index.UN_TOKENIZED));
			        result.add(new Field("internal", link, Field.Store.YES, Field.Index.UN_TOKENIZED));
			        result.add(new Field("title", title, Field.Store.YES, Field.Index.TOKENIZED, Field.TermVector.YES));
			        result.add(new Field("description", description, Field.Store.YES, Field.Index.TOKENIZED, Field.TermVector.YES));
			        
			        result.add(new Field("date", "n/a", Field.Store.YES, Field.Index.UN_TOKENIZED));
			        result.add(new Field("authors", authors, Field.Store.YES, Field.Index.TOKENIZED));
			        result.add(new Field("journal", "n/a", Field.Store.YES, Field.Index.UN_TOKENIZED));
			        
			        result.add(new Field("summary", "n/a", Field.Store.YES, Field.Index.UN_TOKENIZED));
			        result.add(new Field("width", "n/a", Field.Store.YES, Field.Index.UN_TOKENIZED));
			        result.add(new Field("height", "n/a", Field.Store.YES, Field.Index.UN_TOKENIZED));
			        result.add(new Field("fileSize", "n/a", Field.Store.YES, Field.Index.UN_TOKENIZED));
			        result.add(new Field("fileFormat", "n/a", Field.Store.YES, Field.Index.UN_TOKENIZED));
			        result.add(new Field("thumbUrl", "n/a", Field.Store.YES, Field.Index.UN_TOKENIZED));
			        result.add(new Field("thumbWidth", "n/a", Field.Store.YES, Field.Index.UN_TOKENIZED));
			        result.add(new Field("thumbHeight", "n/a", Field.Store.YES, Field.Index.UN_TOKENIZED));
			        
			        result.setBoost(300 / (i + 1));
			        			        
			        documentArray.add(result);
			        if (documentArray.size() == 10) {
					    this.storage.performIOAction(Storage.WRITE_ACTION, documentArray);
					    this.storage.updateGrid();
					    documentArray.clear();
			        }
		    	}
		    }
		    
		    this.storage.performIOAction(Storage.WRITE_ACTION, documentArray);
		    this.storage.updateGrid();
		    
		  } catch (HttpException e) {
	        System.err.println("Fatal protocol violation: " + e.getMessage());
	        e.printStackTrace();
	      } catch (IOException e) {
	        System.err.println("Fatal transport error: " + e.getMessage());
	        e.printStackTrace();
	      } catch (SAXNotRecognizedException e) {
			e.printStackTrace();
		} catch (SAXNotSupportedException e) {
			e.printStackTrace();
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (TransformerException e) {
			e.printStackTrace();
		} 

		if (this.stop) {
			this.logger.debug("Thread killed.");
            return;
        }
		
		this.logger.debug("Thread ended.");
    }
	
	public void setQueryString(String queryString) {
		this.queryString = queryString;
	}
	
	public String getQueryString() {
		return this.queryString;
	}
	
	public void setCount(int count) {
		this.count = count;
	}
	
	public int getCount() {
		return this.count;
	}
	
	public void setStorage(Storage storage) {
		this.storage = storage;
	}	
	
	public Storage getStorage() {
		return this.storage;
	}

	public WebContext getWctx() {
		return wctx;
	}

	public void setWctx(WebContext wctx) {
		this.wctx = wctx;
	}
}
